package com.itheima.d1_create_thread;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 目标：掌握线程的创建方式三：实现Callable接口。
 * 多线程的第三种创建方式：利用Callable接口、FutureTask类来实现。
 *  1、创建任务对象
 *          定义一个类实现Callable接口，重写call方法，封装要做的事情，和要返回的数据。
 *          把Callable类型的对象封装成FutureTask（线程任务对象）。
 * 2、把线程任务对象交给Thread对象。
 * 3、调用Thread对象的start方法启动线程。
 * 4、线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。
 *
 * 线程的创建方式一:继承Thread类 ，多线程的创建方式二：实现Runnable接口 这两种创建方式都存在的问题：
 *       假如线程执行完毕后有一些数据需要返回，他们重写的run方法均不能直接返回结果。
 * 怎么解决：
 *      JDK 5.0提供了Callable接口和FutureTask类来实现（多线程的第三种创建方式）。
 *      这种方式最大的优点：可以返回线程执行完毕后的结果。
 *
 * FutureTask的API
 *          FutureTask提供的构造器
 * public FutureTask<>(Callable call)       把Callable对象封装成FutureTask对象。
 *          FutureTask提供的方法
 * public V get() throws Exception          获取线程执行call方法返回的结果。
 *
 * 线程创建方式三的优缺点
 * 优点：线程任务类只是实现接口，可以继续继承类和实现接口，扩展性强；可以在线程执行完毕后去获取线程执行的结果。
 * 缺点：编码复杂一点。
 *
 * 1、请对对比说一下三种线程的创建方式，和不同点？
 *      方式                          优点                                              缺点
 *  继承Thread类       编程比较简单，可以直接使用Thread类中的方法                        扩展性较差，不能再继承其他的类，不能返回线程执行的结果
 *  实现Runnable接口   扩展性强，实现该接口的同时还可以继承其他的类。                      编程相对复杂，不能返回线程执行的结果
 *  实现Callable接口   扩展性强，实现该接口的同时还可以继承其他的类。可以得到线程执行的结果    编程相对复杂
 *
 */
public class ThreadTest3 {
    public static void main(String[] args) throws Exception {
        // 3、创建一个Callable的对象
        Callable<String> call = new MyCallable(100);        //把MyCallable对象交给他的一个父类类型的变量

        // 4、public FutureTask(Callable<V> callable)  把Callable的对象封装成一个FutureTask对象（也就是任务对象）
        /*
         未来任务对象的作用？
         1、是一个任务对象，实现了Runnable对象。就可以交给线程对象的有参构造器来处理了
           public Thread(Runnable target)      封装Runnable对象成为线程对象

         2、可以在线程执行完毕之后，用未来任务对象调用get方法获取线程执行完毕后的结果。

        FutureTask源码：
        public class FutureTask<V> implements RunnableFuture<V>
        RunnableFuture源码：
        public interface RunnableFuture<V> extends Runnable, Future<V>
         */
        FutureTask<String> f1  = new FutureTask<>(call);
        // 5、把任务对象交给一个Thread对象，创建子线程
        new Thread(f1).start();


        //求1-200的和
        Callable<String> call2 = new MyCallable(200);
        FutureTask<String> f2  = new FutureTask<>(call2);
        new Thread(f2).start();


        // 6、获取线程执行完毕后返回的结果。也就是用未来任务对象调用get方法获取线程执行完毕后的结果，也就是获得call方法中的结果
        //调用未来任务对象的get方法，拿的是这个call方法的返回结果，而这个call方法返回的结果可能会有异常，所以还要抛出异常
        // 注意：如果执行到这儿，假如上面的线程还没有执行完毕，也就是说怎么确定现在可以用未来任务对象调用get方法获取线程执行完毕后的结果
        //解释：这里的代码会暂停，等待上面线程执行完毕后才会获取结果。
        String rs = f1.get();
        System.out.println(rs);

        String rs2 = f2.get();
        System.out.println(rs2);
    }
}
